﻿using System;
using System.Collections.Generic;
using System.Text;
using ITAP.Core.Modelling.Primitives;
using ITAP.ShprossesBuilder.Calculations;
using ITAP.Core.Modelling;
using ITAP.Core.Calculations;

namespace ITAP.ShprossesBuilder.Modelling.Components.Constraints
{
    [Serializable]
    class ConnectedComponentsStickedEndsConstraint : IConstraint
    {
        private bool enabled = true;
        private Type typeOfConnectedComponents;

        public ConnectedComponentsStickedEndsConstraint(Type typeOfConnectedComponents)
        {
            this.typeOfConnectedComponents = typeOfConnectedComponents;
        }

        #region IConstraint Members

        public bool Check(IComponent component)
        {
            //if (!(component is IEndedComponent)) return true;
            //IEndedComponent endedComponent = component as IEndedComponent;

            foreach (IComponent connectedComponent in component.ConnectedComponents)
            {
                if (!(connectedComponent is IEndedComponent)) continue;
                if (connectedComponent.GetType() != typeOfConnectedComponents) continue;

                IEndedComponent endedConnectedComponent = connectedComponent as IEndedComponent;
                if (component is IEndedComponent)
                {
                    // Если компоненты соединены "конец в конец", то ограничение не работает.
                    if (((IEndedComponent)component).StartConnectedComponent == connectedComponent)
                        continue;

                    if (((IEndedComponent)component).EndConnectedComponent == connectedComponent)
                        continue;

                }
                // Если к компоненту приконнекчено начало компонента
                if (endedConnectedComponent.StartConnectedComponent == component)
                {

                    // TODO Прилипание пока работает только к внутренней части компонента - к UpBorder, поэтому берём в качестве точки прилипания точку пересечения с UpBorder - индекс в массиве 2.
                    StickConnectionType stickType = endedConnectedComponent.AcceptedComponentTypesToConnect.GetStickType(component.GetType());
                    StickConnectionType stickedTo;

                    IComponent innerComponent;

                    //ModelVector vector = endedConnectedComponent.Primitives[0].GetTangent(0);
                    //ModelPoint measurePoint = ModelCalculations.GetOffsetPoint(endedConnectedComponent.StartPoint, vector.Multiply(100000));                    

                    ModelPoint nearestPointOnComponent = null;

                    if (endedConnectedComponent is StickConnector)
                    {
                        nearestPointOnComponent = component.GetNearestPointOnComponent(endedConnectedComponent.StartPoint,
                            endedConnectedComponent.EndPoint, stickType, out stickedTo, out innerComponent, ProjectionType.Intersection);

                        ((StickConnector)endedConnectedComponent).Sticked = stickedTo;
                    }
                    else if (endedConnectedComponent is SpacerConnector)
                    {
                        ModelVector vector = endedConnectedComponent.Primitives[0].GetTangent(0);
                        ModelPoint measurePoint = ModelCalculations.GetOffsetPoint(endedConnectedComponent.StartPoint, vector.Multiply(1));                    

                        nearestPointOnComponent = component.GetNearestPointOnComponent(endedConnectedComponent.StartPoint,
                            measurePoint, stickType, out stickedTo, out innerComponent, ProjectionType.Intersection);

                        if (innerComponent is SpacerElement)
                        {
                            ((SpacerConnector)endedConnectedComponent).ConnectedSpacerElement = innerComponent as SpacerElement;
                        }
                    }
                    else throw new NotImplementedException();
                    //if(endedConnectedComponent is SpacerConnector)
                    //    ((SpacerConnector)endedConnectedComponent).ConnectedSpacerElement = 

                    //ModelPoint nearestPointOnComponent = ((IEndedPrimitive)endedComponent.Primitives[0]).GetProjectionPoint(endedConnectedComponent.StartPoint)[2];
                    if (nearestPointOnComponent == null)
                        continue;

                    if (!nearestPointOnComponent.Equals(endedConnectedComponent.StartPoint))
                        return false;

                }
                // Если к компоненту приконнекчен конец компонента
                else if (endedConnectedComponent.EndConnectedComponent == component)
                {
                    //// TODO Прилипание пока работает только к внутренней части компонента - к UpBorder, поэтому берём в качестве точки прилипания точку пересечения с UpBorder - индекс в массиве 2.
                    //StickConnectionType stickType = endedConnectedComponent.AcceptedComponentTypesToConnect.GetStickType(component.GetType());

                    ////ModelPoint nearestPointOnComponent = component.GetNearestPointOnComponent(endedConnectedComponent.EndPoint, stickType);
                    //ModelPoint nearestPointOnComponent = component.GetNearestPointOnComponent(endedConnectedComponent.EndPoint, stickType);
                    ////ModelPoint nearestPointOnComponent = ((IEndedPrimitive)endedComponent.Primitives[0]).GetProjectionPoint(endedConnectedComponent.EndPoint)[2];
                    //if (nearestPointOnComponent == null) continue;

                    //if (!nearestPointOnComponent.Equals(endedConnectedComponent.EndPoint))
                    //    return false;

                }
                else
                    continue;
                //throw new Exception();

            }
            return true;
        }

        public void Fix(IComponent component)
        {
            if (!enabled) return;
            //   if (!(component is IEndedComponent)) return;
            // IEndedComponent endedComponent = component as IEndedComponent;

            foreach (IComponent connectedComponent in component.ConnectedComponents)
            {
                if (!(connectedComponent is IEndedComponent)) continue;
                if (connectedComponent.GetType() != typeOfConnectedComponents) continue;

                IEndedComponent endedConnectedComponent = connectedComponent as IEndedComponent;

                // Если к компоненту приконнекчено начало компонента
                if (endedConnectedComponent.StartConnectedComponent == component)
                {
                    //ModelPoint nearestPointOnComponent = ((IEndedPrimitive)endedComponent.Primitives[0]).GetProjectionPoint(endedConnectedComponent.StartPoint)[2];
                    StickConnectionType stickType = endedConnectedComponent.AcceptedComponentTypesToConnect.GetStickType(component.GetType());
                    StickConnectionType sticked;
                    //ModelPoint nearestPointOnComponent = component.GetNearestPointOnComponent(endedConnectedComponent.StartPoint, stickType);
                    //                    ModelVector vector = endedConnectedComponent.Primitives[0].GetTangent(0);
                    //                    ModelPoint measurePoint = ModelCalculations.GetOffsetPoint(endedConnectedComponent.StartPoint, vector.Multiply(100000));
                    ModelPoint nearestPointOnComponent = null;

                    StickConnectionType stickedTo;

                    IComponent innerComponent;

                    if (endedConnectedComponent is StickConnector)
                    {
                        nearestPointOnComponent = component.GetNearestPointOnComponent(endedConnectedComponent.StartPoint,
                            endedConnectedComponent.EndPoint, stickType, out stickedTo, out innerComponent, ProjectionType.Intersection);

                        ((StickConnector)endedConnectedComponent).Sticked = stickedTo;
                    }
                    else if (endedConnectedComponent is SpacerConnector)
                    {
                        ModelVector vector = endedConnectedComponent.Primitives[0].GetTangent(0);
                        ModelPoint measurePoint = ModelCalculations.GetOffsetPoint(endedConnectedComponent.StartPoint, vector.Multiply(1));

                        nearestPointOnComponent = component.GetNearestPointOnComponent(endedConnectedComponent.StartPoint,
                            measurePoint, stickType, out stickedTo, out innerComponent, ProjectionType.Intersection);
                    }
                    else throw new NotImplementedException();
                                           
                    if (endedConnectedComponent is StickConnector)
                        (endedConnectedComponent as StickConnector).Sticked = stickedTo;
                    if (nearestPointOnComponent == null)
                        continue;

                    if (!nearestPointOnComponent.Equals(endedConnectedComponent.StartPoint))
                    {
                        endedConnectedComponent.MoveWithOffset(new ModelVector(endedConnectedComponent.StartPoint, nearestPointOnComponent));
                        // endedConnectedComponent.StartPoint = (ModelPoint)nearestPointOnComponent.Clone();
                    }

                }
                // Если к компоненту приконнекчен конец компонента
                else if (endedConnectedComponent.EndConnectedComponent == component)
                {
                    ////ModelPoint nearestPointOnComponent = ((IEndedPrimitive)endedComponent.Primitives[0]).GetProjectionPoint(endedConnectedComponent.EndPoint)[2];
                    //StickConnectionType stickType = endedConnectedComponent.AcceptedComponentTypesToConnect.GetStickType(component.GetType());

                    ////ModelPoint nearestPointOnComponent = component.GetNearestPointOnComponent(endedConnectedComponent.EndPoint, stickType);
                    //ModelPoint nearestPointOnComponent = component.GetNearestPointOnComponent(endedConnectedComponent.EndPoint, stickType);
                    //if (nearestPointOnComponent == null) 
                    //    continue;

                    //if (!nearestPointOnComponent.Equals(endedConnectedComponent.EndPoint))
                    //{
                    //    endedConnectedComponent.MoveWithOffset(new ModelVector(endedConnectedComponent.EndPoint, nearestPointOnComponent));
                    //  //  endedConnectedComponent.EndPoint = (ModelPoint)nearestPointOnComponent.Clone();
                    //}                     
                }
                else
                    continue;
                //throw new Exception();

            }
        }

        public bool Enabled
        {
            get
            {
                return enabled;
            }
            set
            {
                enabled = value;
            }
        }

        public ConstraintAction Action
        {
            get { return ConstraintAction.Fix; }
        }

        #endregion

        #region ICloneable Members

        public object Clone()
        {
            ConnectedComponentsStickedEndsConstraint clone = new ConnectedComponentsStickedEndsConstraint(typeOfConnectedComponents);
            return clone;
        }

        #endregion
    }
}
